package com.rainyun.rc.util;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**类加载/资源加载工具*/
public class RainLoader {


	/**添加jar到加载器*/
	public static boolean addJarToLoader(File jar,ClassLoader classLoader){
		try {
			URL url = jar.toURI().toURL();
			Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
			method.setAccessible(true);
			method.invoke(classLoader, url);
			return true;
		} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException|MalformedURLException e) {
			e.printStackTrace();
		}
		return false;
	}


	/**载入和该对象的类相同项目下的所有类
	 * @param obj 对象
	 * @param classLoader 类加载器
	 * */
	public static @NotNull List<Class<?>> loadClassesByObj(@NotNull Object obj, ClassLoader classLoader) throws IOException {
		return loadClassesByClass(obj.getClass(),classLoader);
	}

	/**载入和该类相同项目的所有类
	 * @param c 该类
	 * @param classLoader 类加载器
	 * */
	public static @NotNull List<Class<?>> loadClassesByClass(@NotNull Class<?> c, ClassLoader classLoader) throws IOException {
		String path = c.getProtectionDomain().getCodeSource().getLocation().getPath();
		if(path.endsWith(".jar")){
			return loadJarClasses(path,classLoader);
		}else{
			return loadFolderClasses(path,classLoader);
		}
	}

	/**载入jar的所有类*/
	public static @NotNull List<Class<?>> loadJarClasses(String jarFile, ClassLoader classLoader) throws IOException {
		List<Class<?>> classes = new ArrayList<>();
		File f = new File(jarFile);
		JarFile jar = new JarFile(jarFile);
		Enumeration<JarEntry> enumFiles = jar.entries();
		JarEntry entry;
		while(enumFiles.hasMoreElements()){
			entry = enumFiles.nextElement();
			String classFullName = entry.getName();
			if(classFullName.endsWith(".class")){
				String className = classFullName.substring(0,classFullName.length()-6).replace("/", ".");
				try {
					classes.add(classLoader.loadClass(className));
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				}
			}
		}
		jar.close();
		return classes;
	}

	/**
	 *  载入文件夹内的所有类(自动识别路径下的结构为包名)
	 */
	public static @NotNull List<Class<?>> loadFolderClasses(String filePath, ClassLoader classloader) {
		filePath = new File(filePath).getAbsolutePath();
		List<Class<?>> classes = new ArrayList<Class<?>>();
		@NotNull List<File> files = getFolderFiles(filePath);
		for (File file : files) {
			String path = file.getPath();
			if(!path.endsWith(".class"))
				continue;
			path = path.substring(filePath.length()+1,path.lastIndexOf("."));
			path = path.replace("\\", ".");
			try {
				classes.add(classloader.loadClass(path));
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		}
		return classes;
	}
	/**获取文件夹下所有单文件*/
	public static @NotNull List<File> getFolderFiles(String filePath){
		List<File> files = new ArrayList<>();
		File file = new File(filePath);
		File[] childFiles = file.listFiles();
		for (File childFile : childFiles) {
			if (childFile.isDirectory()) {
				files.addAll(getFolderFiles(childFile.getPath()));
			} else {
				files.add(childFile);
			}
		}
		return files;
	}

	/**获取当前加载器的资源路径下的所有资源文件
	 * @param path 资源路径*/
	public static List<String> getResourceFiles(String path, ClassLoader classLoader) throws IOException {
		URL respath = classLoader.getResource(path);
		if(respath==null)return null;
		String p = respath.getProtocol();
		if("file".equals(p)){
			@NotNull List<File> fs = getFolderFiles(respath.getPath());
			List<String> fstr = new ArrayList<>();
			if(fs!=null)
			for (File f:fs)
				fstr.add(f.getPath());
			return fstr;
		}else if("jar".equals(p)){
			return getJarFiles(path,classLoader);
		}
		return null;
	}
	/**获取加载器jar内的各资源文件路径*/
	public static List<String> getJarFiles(String path,ClassLoader classloader) throws IOException {
		path =  path.replace("\\","/");
		List<String> paths = new LinkedList<String>();
		JarFile jarFile  = null;
		try {
			URL url = classloader.getResource(path);
			JarURLConnection jarURLConnection  = (JarURLConnection)url.openConnection();
			jarFile = jarURLConnection.getJarFile();
		} catch (IOException e) {
			e.printStackTrace();
		}
		Enumeration<JarEntry> jarEntries = jarFile.entries();
		while (jarEntries.hasMoreElements()) {
			JarEntry jarEntry = jarEntries.nextElement();
			String name = jarEntry.getName();
			if(name.startsWith(path))
			if(name.lastIndexOf(".")>=
					name.replace("\\","/").lastIndexOf("/"))
				paths.add(name);
		}
		jarFile.close();
		return paths;
	}
}
